package com.jboltai.resource.ai;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSON;
import com.jboltai.config.JBoltAIConfig;
import com.jboltai.event.ai.aichat.AIChatEvent;
import com.jboltai.resource.Resource;
import okhttp3.OkHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.Proxy;
import java.util.Calendar;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerArray;

public class AIResource extends Resource<AIChatEvent> {

    private static final Logger logger = LoggerFactory.getLogger(JBoltAIConfig.LOGGER_NAME);

    /**
     * 默认的并发请求数
     */
    private static final int DEFAULT_REQUEST_CONCURRENT_COUNT = 20;

    /**
     * 模型
     */
    private AIModel model;

    /**
     * 它所属的schedule
     */
    private AIResourceGroup resourceGroup;

    /**
     * 默认使用的模型名称
     */
    private String defaultModelName;

    /**
     * 模型的apiKey
     */
    private String apiKey;

    /**
     * 模型的请求地址
     */
    private String apiUrl;

    /**
     * 应用id
     */
    private String appId;

    /**
     * 应用的secret
     */
    private String appSecret;

    /**
     * 代理地址
     */
    private Proxy proxy;


    private OkHttpClient client;

    /**
     * 同一个域名，同一个httpclient可以并发执行的异步请求数，超过的就会进等待队列
     */
    private int maxAsyncRequestPerHost = JBoltAIConfig.getMaxAsyncRequestPerHost();

    /**
     * 同一个httpclient可以并发执行的异步请求数，超过的就会进等待队列
     */
    private int maxAsyncRequestPerClient = JBoltAIConfig.getMaxAsyncRequestPerClient();

    /**
     * 思考过程是否可控
     */
    private boolean isThinkingControllable;

    /**
     * 并发请求数限制,3个值分别对应 每分钟 每小时 每天
     */
    private AtomicIntegerArray requestLimit = new AtomicIntegerArray(new int[]{Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE,});

    private volatile AtomicIntegerArray remainLimit = new AtomicIntegerArray(new int[]{Integer.MAX_VALUE, Integer.MAX_VALUE, Integer.MAX_VALUE,});

    /**
     * 上次重置时间
     */
    private long[] lastResetLimitTime = new long[]{System.currentTimeMillis(), System.currentTimeMillis(), System.currentTimeMillis()};

    /**
     * 该实例化函数会使用随机id
     *
     * @param model  哪个模型
     * @param apiKey 该模型使用的api key
     */
    public AIResource(AIModel model, String apiKey) {
        this(model, "ai-" + IdUtil.fastSimpleUUID(), apiKey);
    }


    /**
     * @param model  哪个模型
     * @param id     指定一个唯一性id，后续可根据id对该资源增删改查
     * @param apiKey 该模型使用的api key
     */
    public AIResource(AIModel model, String id, String apiKey) {
        super(id);
        this.model = model;
        this.defaultModelName = model.getDefaultModel();
        this.apiKey = apiKey;

    }

    /**
     * @param model  哪个模型
     * @param id     指定一个唯一性id，后续可根据id对该资源增删改查
     * @param apiKey 该模型使用的api key
     */
    public AIResource(AIModel model, String id, String apiKey, String secretKey) {
        super(id);
        this.model = model;
        this.defaultModelName = model.getDefaultModel();
        this.apiKey = apiKey;
        this.appSecret = secretKey;
    }

    /**
     * @param model 哪个模型
     */
    public AIResource(AIModel model, String id, String appId, String apiKey, String secretKey) {
        super(id);
        this.model = model;
        this.defaultModelName = model.getDefaultModel();
        this.appId = appId;
        this.apiKey = apiKey;
        this.appSecret = secretKey;
    }

    /**
     * 设置模型的请求频率限制，尽量不要在使用过程中修改该值
     *
     * @param timeUnit
     * @param limit
     * @return
     */
    public AIResource setRequestLimit(TimeUnit timeUnit, int limit) {
        //先更新请求限制值，更新完，拿到原值，计算新的值和原值的差值，根据差值更新remainList
        synchronized (this) {
            switch (timeUnit) {
                case MINUTES:
                    remainLimit.addAndGet(0, limit - requestLimit.getAndSet(0, limit));
                    break;
                case HOURS:
                    remainLimit.addAndGet(1, limit - requestLimit.getAndSet(1, limit));
                    break;
                case DAYS:
                    remainLimit.addAndGet(2, limit - requestLimit.getAndSet(2, limit));
                    break;
            }
            if (this.resourceGroup != null) {
                this.resourceGroup.update(this);
            }
        }

        return this;
    }


    /**
     * 检测当前资源是否可用
     *
     * @return
     */
    public boolean checkAvailable() {
        return remainLimit.get(0) > 0 && remainLimit.get(1) > 0 && remainLimit.get(2) > 0;
    }


    /**
     * 检测当前资源是否可用，并且减少可用次数
     *
     * @return -1表示不可用 0表示可用，但需要马上回收 1代表可继续使用
     */
    public int checkAvailableAndReduce() {
        boolean availableAfterReduce = true;
        int remain = 0;
        for (int i = 0; i < remainLimit.length(); i++) {
            remain = remainLimit.addAndGet(i, -1);
            if (remain < 0) {
                return -1;
            } else if (remain == 0) {
                availableAfterReduce = false;
            }
        }
        return availableAfterReduce ? 1 : 0;
    }

    /**
     * 重置资源的频率限制
     */
    public void resetLimit() {
        long nowTime = System.currentTimeMillis();

        remainLimit.set(0, requestLimit.get(0));
        lastResetLimitTime[0] = nowTime;

        Calendar now = Calendar.getInstance();
        //每小时执行一次
        if (now.get(Calendar.MINUTE) == 0 || (nowTime - lastResetLimitTime[1] > 3600000)) {
            remainLimit.set(1, requestLimit.get(1));
            lastResetLimitTime[1] = nowTime;
        }
        //每天执行一次
        if ((now.get(Calendar.HOUR_OF_DAY) == 0 && now.get(Calendar.MINUTE) == 0) || (nowTime - lastResetLimitTime[2] > 86400000)) {
            remainLimit.set(2, requestLimit.get(2));
            lastResetLimitTime[2] = nowTime;
        }

        this.getResourceGroup().update(this);
        logger.debug("重置AI资源[{}]的频率限制：{}", this.getId(), JSON.toJSONString(remainLimit));

    }


    public AIModel getModel() {
        return model;
    }


    public String getApiKey() {
        return apiKey;
    }

    public AIResource setApiKey(String apiKey) {
        this.apiKey = apiKey;
        return this;
    }

    public String getApiUrl() {
        return apiUrl;
    }

    public AIResource setApiUrl(String apiUrl) {
        this.apiUrl = apiUrl;
        return this;
    }

    public Proxy getProxy() {
        return proxy;
    }

    public AIResource setProxy(Proxy proxy) {
        if (this.client != null) {
            throw new RuntimeException("httpclient已运行，不可再设置proxy");
        }
        this.proxy = proxy;
        return this;
    }

    public String getDefaultModelName() {
        return defaultModelName;
    }

    public AIResource setDefaultModelName(String defaultModelName) {
        this.defaultModelName = defaultModelName;
        return this;
    }

    public AIResourceGroup getResourceGroup() {
        return resourceGroup;
    }

    void setResourceGroup(AIResourceGroup resourceGroup) {
        this.resourceGroup = resourceGroup;
    }

    /**
     * 获取并发请求数
     *
     * @return
     */
    public int getConcurrentRequestCount() {
        return this.client.dispatcher().getMaxRequests();
    }

    /**
     * 设置最大并发请求数
     *
     * @param count
     */
    public void setConcurrentRequestCount(int count) {
        this.client.dispatcher().setMaxRequests(count);
        this.client.dispatcher().setMaxRequestsPerHost(count);
    }


    public OkHttpClient getClient() {
        if (client == null) {

            OkHttpClient.Builder builder = new OkHttpClient.Builder().readTimeout(JBoltAIConfig.getHttpReadTimeout(), TimeUnit.MILLISECONDS).callTimeout(JBoltAIConfig.getHttpReadTimeout(), TimeUnit.MILLISECONDS).writeTimeout(JBoltAIConfig.getHttpWriteTimeout(), TimeUnit.MILLISECONDS);
            if (this.proxy != null) {
                builder.proxy(this.proxy);
            }
            client = builder.build();
            //异步请求每个域名允许发起的并发请求数
            client.dispatcher().setMaxRequestsPerHost(maxAsyncRequestPerHost);
            client.dispatcher().setMaxRequests(maxAsyncRequestPerClient);
        }
        return client;
    }

    public String getAppId() {
        return appId;
    }

    public AIResource setAppId(String appId) {
        this.appId = appId;
        return this;
    }

    public String getAppSecret() {
        return appSecret;
    }

    public AIResource setAppSecret(String appSecret) {
        this.appSecret = appSecret;
        return this;
    }

    public boolean isThinkingControllable() {
        return isThinkingControllable;
    }

    public AIResource setThinkingControllable(boolean thinkingControllable) {
        isThinkingControllable = thinkingControllable;
        return this;
    }

    public int getMaxAsyncRequestPerHost() {
        return maxAsyncRequestPerHost;
    }

    public AIResource setMaxAsyncRequestPerHost(int maxAsyncRequestPerHost) {
        this.maxAsyncRequestPerHost = maxAsyncRequestPerHost;
        return this;
    }

    public int getMaxAsyncRequestPerClient() {
        return maxAsyncRequestPerClient;
    }

    public AIResource setMaxAsyncRequestPerClient(int maxAsyncRequestPerClient) {
        this.maxAsyncRequestPerClient = maxAsyncRequestPerClient;
        return this;
    }
}
